#include "CStateGame.h"

#include "CGUIGame.h"
#include "CGridSceneNode.h"
#include "CMinionNode.h"
#include "COverlord.h"
#include "CAblPatrona.h"
#include "CAblHeal.h"
#include "CAbilityBlast.h"
#include "RTSCamera.h"
#include "CNetworkManager.h"
#include "CChatBox.h"
#include "CBattle.h"

#define PI 3.14159265

//! Tempo HACK
#define LIGHTING false

CStateGame::CStateGame(CGameManager * mgr) : CState(mgr)
{
  pointed = 0;
  dmgDisplay = 0;
  minionRoot = smgr->addEmptySceneNode();
}

void CStateGame::init()
{
  // Set up GUI
	gui = new CGUIGame(this);
	gui->init();


  // Create world
  createWorld(512, 512);

  // Show Grid
  if ( SHOW_GRID )
  {
    scene::CGridSceneNode* grid = new scene::CGridSceneNode(smgr->getRootSceneNode(), smgr, -1, 8, 512 );
    grid->setPosition(vector3df(256, 0, 256));
  }

  // Set Camera
  //smgr->addCameraSceneNodeMaya();
  // F up camera. Waste alot of time fixing this
  camera = new RTSCamera(device,smgr->getRootSceneNode(),smgr,-1,1000.0f,10.0f,5.0f);
  camera->setPosition(vector3df(50,100, 100));
  camera->setTarget(vector3df(50, 0, 50));
  // init ability
  //ablCurrent = new CAblPatrona();
  ablBolt = new CAblPatrona();
  ablHeal = new CAblHeal();
  ablBlast = new CAbilityBlast();

  // Set up pointer
  IMesh *mesh = smgr->getMesh("pointer.3DS");
  pointer = smgr->addMeshSceneNode( mesh );

  pointer->setPosition(vector3df(0.0f, 60.0f, 0.0f));
  pointer->setScale(vector3df(0.3f, 0.3f, 0.3f));

  mgr->getChatBox()->setVisible(true);
}

void CStateGame::close()
{
  delete battle;

  smgr->getRootSceneNode()->removeAll();

  mgr->getChatBox()->setVisible(false);

  mgr->getNetworkManager()->leaveRoom();

  delete gui;
}

void CStateGame::run()
{
  // Update cursor / minion stuffs
  CMinionNode *selected = getSelectedMinion();
  position2d<s32> cursor = device->getCursorControl()->getPosition();

  ISceneNode *pointedNode = smgr->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(cursor, 0, false, minionRoot);
  pointed = battle->getMinion( pointedNode );

  vector<CMinionNode*> &vMinion = battle->getMinionList();


  for ( vector<CMinionNode*>::iterator it=vMinion.begin() ; it!=vMinion.end(); it++ )
  {
    (*it)->update();

    #ifndef __LON_DEBUG_MODE__
    // Update their material
    if ( (*it) == selected && (*it) != pointed )
      (*it)->getNode()->setDebugDataVisible(false);
    else if ( (*it) == pointed )
      (*it)->getNode()->setDebugDataVisible(true);
    else
      (*it)->getNode()->setDebugDataVisible(false);
    #endif
  }


  ///Testing
  static MINION_COMMAND prevCommand = CMD_NONE;
  static ISceneNode *rangeIndicator;

  if ( prevCommand != command )
  {
    prevCommand = command;

    if ( rangeIndicator )
    {
      rangeIndicator->remove();
      rangeIndicator = 0;
    }

    switch (command)
    {
      case CMD_ATTACK:
        rangeIndicator = smgr->addSphereSceneNode(selected->getStats().range);
        rangeIndicator->setPosition(selected->getNode()->getPosition());
        rangeIndicator->setMaterialFlag(video::EMF_LIGHTING, LIGHTING);
        rangeIndicator->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);
        rangeIndicator->setMaterialTexture(0, driver->getTexture("attack.png"));
        rangeIndicator->getMaterial(0).BackfaceCulling = false;
        break;

      case CMD_MOVE:
        rangeIndicator = smgr->addSphereSceneNode(selected->getStats().movement);
        rangeIndicator->setPosition(selected->getNode()->getPosition());
        rangeIndicator->setMaterialFlag(video::EMF_LIGHTING, LIGHTING);
        rangeIndicator->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);
        rangeIndicator->setMaterialTexture(0, driver->getTexture("indicator.png"));
        rangeIndicator->getMaterial(0).BackfaceCulling = false;
        break;

      case CMD_ABILITY:
        rangeIndicator = smgr->addSphereSceneNode(ablCurrent->getRange());
        rangeIndicator->setPosition(selected->getNode()->getPosition());
        rangeIndicator->setMaterialFlag(video::EMF_LIGHTING, LIGHTING);
        rangeIndicator->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);
        rangeIndicator->setMaterialTexture(0, driver->getTexture("spell.png"));
        rangeIndicator->getMaterial(0).BackfaceCulling = false;
        break;

      case CMD_NONE:
      default:
        break;
    }
  }
  // Reposition indicator (TEMPORARY)
  else if (rangeIndicator)
  {
    if (rangeIndicator->getPosition() != selected->getNode()->getPosition())
      rangeIndicator->setPosition(selected->getNode()->getPosition());
  }
  /// End testing

  //! Pointer to selected minion
  static f32 pointerYMod = 0.5f;
  vector3df pointerPos = pointer->getPosition();
  pointerPos.Y += pointerYMod;

  if ( pointerPos.Y > 65.0f )
  {
    pointerPos.Y = 65.0f;
    pointerYMod = -pointerYMod;
  }

  if ( pointerPos.Y < 55.0f )
  {
    pointerPos.Y = 55.0f;
    pointerYMod = -pointerYMod;
  }

  pointer->setPosition(pointerPos);

  gui->updateMinion();


  driver->beginScene(true, true, SColor(255,0,0,0));
  smgr->drawAll();

  //! Draw more stuffs here
/** more eye candys
   for ( list<CMinionNode*>::Iterator it=vMinion.begin() ; it!=vMinion.end(); it++ )
  {
    CMinionNode *pMinion = (*it);
    vector3df pos = pMinion->getNode()->getPosition();
    pos.Y += 6;

    position2d<s32> drawPosition = smgr->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(pos);

    //if ( drawPosition.X != -10000 || drawPosition.Y != -10000 )
    //{
      s32 lf = drawPosition.X -52;
      s32 rt = drawPosition.X +52;
      s32 up = drawPosition.Y;
      s32 dn = drawPosition.Y +7;

      //rect<s32> clip = rect<32> (0 , 0, SCREEN_W, SCREEN_H);
      driver->draw2DRectangle(SColor(255, 0, 0, 0), rect<s32> (lf, up, rt, dn));

      lf+=2; rt-=2; up+=2; dn-=2;

      f32 ratio = f32 (pMinion->getHp()) / f32 (pMinion->getMaxHp()) * 100.0;
      rt = lf + s32 (ratio);
      driver->draw2DRectangle(SColor(255, 0, 255, 0), rect<s32> (lf, up, rt, dn));


    //}

    //driver->draw3DBox( (*it)->getNode()->getBoundingBox() );
  }
**/
  env->drawAll();
}

void CStateGame::enable()
{
  gui->setVisible(true);
}

void CStateGame::disable()
{
  gui->setVisible(false);
}

void CStateGame::createWorld(int width, int height)
{
  width += 512;
  height += 512;
  float xScale = (float)width  / 128.0f;
  float zScale = (float)height / 128.0f;

  // Set up world
  smgr->setAmbientLight( SColor(255, 192, 192, 192));

  // add terrain scene node
  terrain = smgr->addTerrainSceneNode("map\\terrain-heightmap.bmp");

  terrain->setPosition(vector3df(-256.0f, 0.0f, -256.0f));
  terrain->setScale(core::vector3df(xScale, 0.2f, zScale));
  terrain->setMaterialFlag(video::EMF_LIGHTING, LIGHTING);

  terrain->setMaterialTexture(0, driver->getTexture("map\\terrain-texture.jpg"));
  terrain->setMaterialTexture(1, driver->getTexture("map\\detailmap3.jpg"));

  terrain->setMaterialType(video::EMT_DETAIL_MAP);
  terrain->scaleTexture(1.0f, 20.0f);

  selector = smgr->createTerrainTriangleSelector(terrain, 0);
  terrain->setTriangleSelector(selector);
  selector->drop();

  //smgr->addLightSceneNode(terrain, vector3df(0,200,0), SColorf(1.0f, 0.2f, 0.2f, 0.2f), 600.0f);

    // create skybox
  driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

  smgr->addSkyBoxSceneNode(
    driver->getTexture("map\\irrlicht2_up.jpg"),
    driver->getTexture("map\\irrlicht2_dn.jpg"),
    driver->getTexture("map\\irrlicht2_lf.jpg"),
    driver->getTexture("map\\irrlicht2_rt.jpg"),
    driver->getTexture("map\\irrlicht2_ft.jpg"),
    driver->getTexture("map\\irrlicht2_bk.jpg"));

  driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
}

void CStateGame::nextMinion()
{
  //camera->pointCameraAtNode(getSelectedMinion()->getNode());

  stringw text = getSelectedMinion()->getStats().name;
  text += L"'s Turn.";
  mgr->getChatBox()->addText(text.c_str());

  pointer->setParent(getSelectedMinion()->getNode());

  command = CMD_NONE;
}

void CStateGame::victoryCheck()
{
  s32 victory = battle->victoryCheck();
  if ( victory != -1 )
  {
    stringw message;
    bool won = false;
    COverlord *player = mgr->getPlayer();
    if ( victory == mgr->getPlayerID() )
    {
      message = L"You have won the battle!";
      won = true;
      player->set_win(player->win() + 1 );
    }
    else
    {
      message = L"You have lost the battle!";
      won = false;
      player->set_lose(player->lose() + 1 );
    }

    // Message
    env->addMessageBox(L"Game Over", message.c_str(), true, EMBF_OK, 0, 1337);

    // Create new bitstream for packet
    BitStream bs;
    bs.Write(u8(ID_LON_GAME_END));
    bs.Write(player->player_id());
    bs.Write(won);

    // Send the packet
    mgr->getNetworkManager()->sendPacket(&bs);
  }
}

/*
bool onKeyDown(EKEY_CODE key)
{
  return false;
}
*/
//! Called when network packets from server arrived
void CStateGame::OnServerPacketReceived(Packet *packet)
{

}

//! Called when network packets from other clients arrived
void CStateGame::OnClientPacketReceived(Packet *packet)
{

  switch (packet->data[0])
  {
    case ID_LON_MINION_ATTACK:
      std::cout << "Packet of Message ID_LON_MINION_ATTACK received." << std::endl;
      ackMinionAttack(new BitStream(packet->data, packet->length, false));
      break;

    case ID_LON_MINION_MOVE:
      std::cout << "Packet of Message ID_LON_MINION_MOVE received." << std::endl;
      ackMinionMove(new BitStream(packet->data, packet->length, false));
      break;

    case ID_LON_MINION_SKILL:
      std::cout << "Packet of Message ID_LON_MINION_SKILL received." << std::endl;
      ackMinionSkill(new BitStream(packet->data, packet->length, false));
      break;

    /// Default RakNet Messages
    case ID_NEW_INCOMING_CONNECTION:
      std::cout << "A player has join the game." << std::endl;
      break;

    case ID_CONNECTION_REQUEST_ACCEPTED:
      std::cout << "Successfully joined game room." << std::endl;
      break;

    case ID_CONNECTION_ATTEMPT_FAILED:
      std::cout << "Failed to join game room." << std::endl;
      break;

    case ID_NO_FREE_INCOMING_CONNECTIONS:
      std::cout << "The game is full." << std::endl;
      break;

    case ID_REMOTE_NEW_INCOMING_CONNECTION:
      std::cout << "A player has joined the game." << std::endl;
      break;

    case ID_REMOTE_DISCONNECTION_NOTIFICATION:
    case ID_REMOTE_CONNECTION_LOST:
      std::cout << "A player has left the game." << std::endl;
      break;

    case ID_DISCONNECTION_NOTIFICATION:
      if (mgr->getNetworkManager()->IsHost())
      {
        std::cout << "A client has disconnected." << std::endl;
      }
      else
      {
        std::cout << "You have been disconnected." << std::endl;
        mgr->popState();
      }
      break;

    case ID_CONNECTION_LOST:
      if (mgr->getNetworkManager()->IsHost()){
        std::cout << "A client lost the connection." << std::endl;
      } else {
        std::cout << "Connection lost." << std::endl;
        mgr->popState();
      }
      break;
    default:
      std::cout << "Message with identifier " << int (packet->data[0]) << " has arrived." << std::endl;
      break;
  }
}

bool CStateGame::OnKeyUp(EKEY_CODE key)
{
  switch (key)
  {
    case KEY_KEY_A:
      command = CMD_ATTACK;
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      return true;
      break;

    case KEY_KEY_M:
      command = CMD_MOVE;
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      return true;
      break;

    case KEY_KEY_C:
      mgr->getChatBox()->setVisible(true);
      return true;

    default:
      break;
  }
  return false;
}


bool CStateGame::OnMouseClick()
{
  CMinionNode *selected = battle->getFirstMinion();

  if ( selected->getOwner()->getID() != mgr->getPlayerID() )
  {
    mgr->getChatBox()->addText(L"It's not your turn now!");
    return false;
  }

  switch (command)
  {
    case CMD_NONE:
      break;

    case CMD_ATTACK:
      // Attack
      OnCommandAttack();
      break;

    case CMD_MOVE:
      OnCommandMove();
      break;

    case CMD_ABILITY:
      OnCommandAbility();
      break;

    default:
      break;
  }
  return false;
}

bool CStateGame::OnButtonClick(s32 id)
{
  switch (id)
  {
    case BTN_ATTACK:
      command = CMD_ATTACK;
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      return true;
      break;

    case BTN_MOVE:
      command = CMD_MOVE;
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      return true;
      break;

    case BTN_CANCEL:
      command = CMD_NONE;
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      return true;
      break;
    case BTN_POPSTATE:
      mgr->popState();
      return true;
      break;

    //! Overlord Spell Button
    case BTN_SPELL:
      //spell mass heal
      //castSpell();
      return true;
      break;

    //! Minion Ability Button
    case BTN_ABILITY_1:
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      ablCurrent = ablBolt;
      if ( ablCurrent->isTargeted() )
        command = CMD_ABILITY;

      else
        OnCommandAbility();
        //ablCurrent->effect();
      return true;
      break;

    case BTN_ABILITY_2:
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      ablCurrent = ablHeal;
      if ( ablCurrent->isTargeted() )
        command = CMD_ABILITY;

      else
        OnCommandAbility();
        //ablCurrent->effect();
      return true;
      break;

    case BTN_ABILITY_3:
      camera->pointCameraAtNode(getSelectedMinion()->getNode());
      ablCurrent = ablBlast;
      if ( ablCurrent->isTargeted() )
        command = CMD_ABILITY;

      else
        OnCommandAbility();
        //ablCurrent->effect();
      return true;
      break;
  }

  return false;
}

bool CStateGame::OnGUIEvent(const SEvent &event)
{
  s32 id = event.GUIEvent.Caller->getID();

  switch(event.GUIEvent.EventType)
  {
    case EGET_MESSAGEBOX_OK :
      if ( id == 1337 )
      {
        mgr->popState();
        return true;
      }
      break;

    default:
      break;
  }

  return false;
}

void CStateGame::reqMinionAttack()
{
  BitStream bs;

  bs.Write(u8(ID_LON_MINION_ATTACK));
  bs.Write(pointed->getID());

  mgr->getNetworkManager()->sendPacket(&bs, false);

  // Process the command if this is the host
  if (mgr->getNetworkManager()->IsHost())
    attack(pointed->getID());

}

void CStateGame::ackMinionAttack(BitStream *bs)
{
  u8 msgId;
  u32 targetID;

  bs->Read(msgId);
  bs->Read(targetID);

  // Attack
  attack(targetID);

  // Broadcast to other clients if this is the host
  if (mgr->getNetworkManager()->IsHost())
    mgr->getNetworkManager()->sendPacket(bs, false);

  delete bs;
}

void CStateGame::reqMinionMove(vector3df intersection)
{
  BitStream bs;
  bs.Write(u8(ID_LON_MINION_MOVE));

  // Write the destination coordinates
  bs.Write(intersection.X);
  bs.Write(intersection.Y);
  bs.Write(intersection.Z);

  mgr->getNetworkManager()->sendPacket(&bs, false);

  cout << "Packet ID_LON_MINION_MOVE Sent" << endl;
  // Process the command if this is the host
  if (mgr->getNetworkManager()->IsHost())
    moveMinion(intersection);
}

void CStateGame::ackMinionMove(BitStream *bs)
{
  u8 msgId;
  f32 x, y, z;

  // Buffer out the message ID
  bs->Read(msgId);

  // Read the coordinates
  bs->Read(x);
  bs->Read(y);
  bs->Read(z);

  // Move the minion
  moveMinion(vector3df(x, y, z));

  // Broadcast to other clients if this is the host
  if (mgr->getNetworkManager()->IsHost())
    mgr->getNetworkManager()->sendPacket(bs, false);

  delete bs;
}


void CStateGame::reqMinionSkill(u32 abilityID)
{
  // Create new bitstream for packet
  BitStream bs;
  bs.Write(u8(ID_LON_MINION_SKILL));
  bs.Write(abilityID);
  bs.Write(ABL_NONE);

  // Send the packet
  mgr->getNetworkManager()->sendPacket(&bs, false);

  // Process the command if this is the host
  if (mgr->getNetworkManager()->IsHost())
    ability(abilityID);
}

void CStateGame::reqMinionSkill(u32 abilityID, u32 targetID)
{
  // Create new bitstream for packet
  BitStream bs;
  bs.Write(u8(ID_LON_MINION_SKILL));
  bs.Write(abilityID);
  bs.Write(ABL_TARGET);

  bs.Write(targetID);

  // Send the packet
  mgr->getNetworkManager()->sendPacket(&bs, false);

  // Process the command if this is the host
  if (mgr->getNetworkManager()->IsHost())
    ability(abilityID, targetID);
}

void CStateGame::reqMinionSkill(u32 abilityID, vector3df intersection)
{
   // Create new bitstream for packet
  BitStream bs;
  bs.Write(u8(ID_LON_MINION_SKILL));
  bs.Write(abilityID);
  bs.Write(ABL_POINT);

  // Write the destination coordinates
  bs.Write(intersection.X);
  bs.Write(intersection.Y);
  bs.Write(intersection.Z);

  // Send the packet
  mgr->getNetworkManager()->sendPacket(&bs, false);

  // Process the command if this is the host
  if (mgr->getNetworkManager()->IsHost())
    ability(abilityID, intersection);
}


void CStateGame::ackMinionSkill(BitStream *bs)
{
  u8 msgId;

  u32 abilityID;
  u32 targetID;
  ABILITY_TYPE ablType;
  f32 x, y, z;


  bs->Read(msgId);
  bs->Read(abilityID);
  bs->Read(ablType);

  switch (ablType)
  {
    case ABL_NONE:
      // No more information is needed
      cout << "No Target Ability" << endl;
      ability(abilityID);
      break;
    case ABL_TARGET:
      // Read the targets
      bs->Read(targetID);
      cout << "Target Ability" << endl;
      ability(abilityID, targetID);
      break;
    case ABL_POINT:
      // Read the coordinates
      cout << "Point Ability" << endl;
      bs->Read(x);
      bs->Read(y);
      bs->Read(z);
      ability(abilityID, vector3df(x, y, z));
      break;
    default:
      std::cout << "FATAL ERROR: Unknown ABILITY_TYPE Received." << std::endl;
      break;
  }

  // Broadcast to other clients if this is the host
  if (mgr->getNetworkManager()->IsHost())
    mgr->getNetworkManager()->sendPacket(bs, false);

  delete bs;
}

void CStateGame::attack(u32 targetID)
{
  CMinionNode *source = battle->getFirstMinion();
  CMinionNode *target = battle->getMinionFromID(targetID);

  // Order to attack
  u16 dmg = source->attack(target);

  // Check if the attack is sucessful
  if ( dmg )
  {
    vector3df pos = target->getNode()->getPosition();
    getSelectedMinion()->face(pos);

    // Check for death
    if ( target->getStats().hp <= 0 )
    {
      battle->removeMinion(target);
      victoryCheck();
    }

    // Update Delay
    battle->updateDelay(source, 1200);

    //! TODO: Fix this hack
    gui->updateDelay(battle->getMinionList());

    command = CMD_NONE;
  }
}

void CStateGame::moveMinion(vector3df target)
{
  getSelectedMinion()->face(target);
  getSelectedMinion()->move(target);

  battle->updateDelay(getSelectedMinion(), 600);

  gui->updateDelay(battle->getMinionList());

  command = CMD_NONE;
}

void CStateGame::ability(u32 abilityID)
{
  CMinionNode *caster = battle->getFirstMinion();

  ablHeal->setCaster(caster);

  if (ablHeal->effect())
  {
    // Update Delay
    battle->updateDelay(caster, ablHeal->getDelay());
    gui->updateDelay(battle->getMinionList());

    //! TODO: Hardcoded effect should be avoided

    IParticleSystemSceneNode* ps = 0;
    ps = smgr->addParticleSystemSceneNode(false, caster->getNode());
    ps->setPosition(core::vector3df(0,0,0));
    ps->setScale(core::vector3df(2,2,2));

    ps->setParticleSize(core::dimension2d<f32>(20.0f, 10.0f));

    scene::IParticleEmitter* em = ps->createBoxEmitter(
      core::aabbox3d<f32>(-7,0,-7,7,1,7),
      core::vector3df(0.0f,0.03f,0.0f),
      80,100,
      video::SColor(0,255,255,255), video::SColor(0,255,255,255),
      800,2000);

    ps->setEmitter(em);
    em->drop();

    scene::IParticleAffector* paf =
      ps->createFadeOutParticleAffector();

    ps->addAffector(paf);
    paf->drop();

    ps->setMaterialFlag(video::EMF_LIGHTING, false);
    ps->setMaterialTexture(0, driver->getTexture("particle.bmp"));
    ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);

    ISceneNodeAnimator* dele = smgr->createDeleteAnimator(1500);
    ps->addAnimator(dele);
    dele->drop();
  }
}

void CStateGame::ability(u32 abilityID, u32 targetID)
{
  CMinionNode *caster = battle->getFirstMinion();
  CMinionNode *target = battle->getMinionFromID(targetID);

  ablBolt->setCaster(caster);
  ablBolt->setTarget(target);

  if ( ablBolt->effect() )
  {
    // Check for death
    if ( target->getStats().hp == 0 )
    {
      battle->removeMinion(target);
      victoryCheck();
    }

    // Update Delay
    battle->updateDelay(caster, ablBolt->getDelay());
    gui->updateDelay(battle->getMinionList());

    //! TODO: Hardcoded effect should be avoided

    // billboard - particle

    ISceneNode *node = smgr->addBillboardSceneNode(0, dimension2d<f32>(20, 20));
    node->setMaterialFlag(EMF_LIGHTING, false);
    node->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);
    node->setMaterialTexture(0, driver->getTexture("particlewhite.bmp"));

    ISceneNodeAnimator* anim = smgr->createFlyStraightAnimator(caster->getNode()->getPosition(), target->getNode()->getPosition(), 300,  false);
    ISceneNodeAnimator* dele = smgr->createDeleteAnimator(300);
    node->addAnimator(anim);
    node->addAnimator(dele);
    anim->drop();
    dele->drop();
  }
}

void CStateGame::ability(u32 abilityID, vector3df target)
{
  CMinionNode *caster = battle->getFirstMinion();

  ablBlast->setCaster(caster);
  ablBlast->setPoint(target);
  ablBlast->setBattle(getBattle());

  if (ablBlast->effect())
  {
    victoryCheck();

    // Update Delay
    battle->updateDelay(caster, ablBlast->getDelay());
    gui->updateDelay(battle->getMinionList());

    //! TODO: Hardcoded effect should be avoided

    IParticleSystemSceneNode* ps = 0;
    ps = smgr->addParticleSystemSceneNode(false);
    ps->setPosition(target);
    ps->setScale(core::vector3df(2,2,2));

    ps->setParticleSize(core::dimension2d<f32>(15.0f, 15.0f));

    scene::IParticleEmitter* em = ps->createSphereEmitter  ( vector3df(0,0,0),
      5.0f,
      vector3df(0,0,0),
      300, 400,
      SColor(255, 128, 128, 128),  SColor(255, 255, 255, 255),
      500, 1000,
      0,
      dimension2df(15.0f, 15.0f), dimension2df(15.0f, 15.0f)
     );


/*
    scene::IParticleEmitter* em = ps->createPointEmitter  ( vector3df(0,0,0),
      150, 200,
      SColor(255, 128, 128, 128),  SColor(255, 255, 255, 255),
      2000, 4000,
      0,
      dimension2df(15.0f, 15.0f), dimension2df(15.0f, 15.0f)
    );
    */



    ps->setEmitter(em);
    em->drop();

    scene::IParticleAffector* paf =
      ps->createAttractionAffector  (target, 300.0f, false );


    ps->addAffector(paf);
    paf->drop();

    ps->setMaterialFlag(video::EMF_LIGHTING, false);
    ps->setMaterialTexture(0, driver->getTexture("particlered.bmp"));
    ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );

    ISceneNodeAnimator* dele = smgr->createDeleteAnimator(1000);
    ps->addAnimator(dele);
    dele->drop();
  }



}

void CStateGame::drawFloatingText(vector3df location, stringw text, SColor textColor)
{
  IGUIFont *font = env->getFont("myfont.xml");

  dmgDisplay = smgr->addTextSceneNode( font, text.c_str(), textColor );

  // Animator
  location.Y += 60;

  vector3df endPos = location;
  endPos.Y += 10;

  ISceneNodeAnimator* aim = smgr->createFlyStraightAnimator(location, endPos, 1000,  false);
  ISceneNodeAnimator* del = smgr->createDeleteAnimator(1000);

  dmgDisplay->addAnimator(aim);
  dmgDisplay->addAnimator(del);

  aim->drop();
  del->drop();
}

void CStateGame::OnCommandAttack()
{
  CMinionNode *selected = getSelectedMinion();

  if ( pointed != 0 && pointed != getSelectedMinion() )
  {
    if ( selected->IsInRange(pointed, selected->getStats().range) )
    {
      reqMinionAttack();
    }
  }
}

void CStateGame::OnCommandMove()
{
  CMinionNode *selected = getSelectedMinion();
  position2di cursor = device->getCursorControl()->getPosition();
  line3df line = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates( cursor );
  vector3df intersection;
  vector3df sourcePos = selected->getNode()->getPosition();
  triangle3df tri;
  const ISceneNode *outNode;
  //intersectsWithBox (const aabbox3d< T > &other) const

  if (smgr->getSceneCollisionManager()->getCollisionPoint(line, selector, intersection, tri, outNode))
  {
    // Decide to move
    if ( sourcePos.getDistanceFrom(intersection) > selected->getStats().movement )
    {
      std::cout << "You cant move that far." << std::endl;
      return;
    }
  }

  // Check collision
  //! TODO: Need a better collision checking moethod, bounding box is unaccurate

  //Temporary move to destination for colission check
  aabbox3d<f32> sourceBox = selected->getNode()->getTransformedBoundingBox();
  vector3df moveVec = intersection - selected->getNode()->getPosition();
  sourceBox.MaxEdge += moveVec;
  sourceBox.MinEdge += moveVec;

  vector<CMinionNode*> &vMinion = battle->getMinionList();
  for ( vector<CMinionNode*>::iterator it=vMinion.begin() ; it!=vMinion.end(); it++ )
  {
    if ( (*it) == selected )
      continue;

    aabbox3d<f32> targetBox = (*it)->getNode()->getTransformedBoundingBox();
    if ( sourceBox.intersectsWithBox(targetBox) )
    {
      cout << "Other minioned blocked your way." << endl;
      return;
    }
  }

  reqMinionMove(intersection);
}

void CStateGame::OnCommandAbility()
{
  CMinionNode *selected = getSelectedMinion();
  u32 abilityID = 0; //! For future usage

  position2di cursor = device->getCursorControl()->getPosition();
  line3df line = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates( cursor );
  vector3df intersection;
  vector3df sourcePos = selected->getNode()->getPosition();
  triangle3df tri;
  const ISceneNode* outNode;

  switch (ablCurrent->getType())
  {
    // for ABL_NONE
    case ABL_NONE:
      reqMinionSkill(abilityID);
      break;

    // for ABL_TARGET
    case ABL_TARGET:
      if ( pointed != 0 && pointed != getSelectedMinion() )
      {
        if ( selected->IsInRange(pointed, ablCurrent->getRange()) )
        {
          reqMinionSkill(abilityID, pointed->getID());
        }
      }
      break;

    // for ABL_POINT
    case ABL_POINT:
      if (smgr->getSceneCollisionManager()->getCollisionPoint(line, selector, intersection, tri, outNode))
      {
        if ( selected->IsInRange(intersection, ablCurrent->getRange()) )
        {
          reqMinionSkill(abilityID, intersection);
        }
        else
        {
          std::cout << "You can cast so far." << std::endl;
        }
      }
      break;

    case ABL_PASSIVE:
    default:
      std::cout << "ERROR: Invalid ABILITY_TYPE Dectected." << std::endl;
      break;
  }
}

void CStateGame::setBattle(CBattle* battle)
{
  this->battle = battle;
}

CBattle * CStateGame::getBattle()
{
  return battle;
}

CMinionNode * CStateGame::getSelectedMinion()
{
  return battle->getFirstMinion();
}

CMinionNode * CStateGame::getPointedMinion()
{
  return pointed;
}

